using System;
using System.Collections.Generic;
using System.Timers;
using DC2010.Objects.Common;
using DC2010.Objects.Data;
using System.Linq;

namespace DC2010.Objects.Logic
{
    public class CreatureLogic : BaseLogic
    {
        public CreatureInfo Info { get; set; }
        public CreatureGfx Gfx { get; set; }

        public event EventHandler OnDie;
		
		//Timers
		Timer tTenSecondUpdateTimer;

        public CreatureLogic(CreatureData cd)
        {
            // Prepare inner data structures
            Info = Root.I.CreatureSystem.GetInfo(cd.InfoId);
            Gfx = Root.I.CreatureSystem.GetGfx(cd.GfxId);
            Data = cd;
        }
		
		public override void Start()
		{
			// while sleeping interval should be 2500!
            tTenSecondUpdateTimer = new Timer(10000);
            tTenSecondUpdateTimer.Elapsed += delegate
            {
				TenSecondUpdate();
            };
            tTenSecondUpdateTimer.Start();	
		}

        public void Die()
        {
            if (OnDie != null)
                OnDie(null, null);

            Root.I.EffectSystem.Instances.I_Die_Effect.Instant(Data, null);
        }

        public void Ressurect()
        {
            Root.I.EffectSystem.Instances.I_Ressurect_Effect.Instant(Data, null);
        }

        public double CalculateWeight()
        {
           double sumWeight = 0;
           sumWeight += ((CreatureData)Data).Inventory.CalculateWeight();    // inventory
           
           return sumWeight;
        }

        public bool GetItemOwnerShip(string item)
        {
            bool bInv = Info.Inventory.IsItemInInventory(item);
            return bInv;
        }

        public List<string> GetCumulatedBuffs()
        {
            List<string> ls = new List<string>();

            foreach (KeyValue sef in Info.Effects)
            {
                EffectData ed = Root.I.EffectSystem.GetData(sef.Value);

                if ((ed.IsActive) && (ed.IsBuff))
                {
                    //TODO:: Check if this works
                    string ss = ed.L.GetType().ToString();
                    string[] sField = ss.Split('.');

                    string sEffect = sField[sField.GetLength(0) - 1];
                    sEffect = sEffect.Replace("_Effect", "");

                    ls.Add(sEffect);
                }
            }

            //Get attribute changes
            if (Info.Attributes.Health.Boosted) ls.Add("I_IncreaseStat_HEALTH");
            if (Info.Attributes.Stamina.Boosted) ls.Add("I_IncreaseStat_STAMINA");
            if (Info.Attributes.Mana.Boosted) ls.Add("I_IncreaseStat_MANA");
            if (Info.Attributes.Luck.Boosted) ls.Add("I_IncreaseStat_LUCK");
            if (Info.Attributes.Strength.Boosted) ls.Add("I_IncreaseStat_STR");
            if (Info.Attributes.Dexterity.Boosted) ls.Add("I_IncreaseStat_DEX");
            if (Info.Attributes.Vitality.Boosted) ls.Add("I_IncreaseStat_VIT");

            return ls;
        }

        /// <summary>
        /// Method will pick up item.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool PickupItem(ItemData id)
        {
            Root.I.GameSystem.ItemInHand = id;
            Root.I.EventSystem.Instances.E_PickupItem_Event.Invoke(this, id);
            return true;
        }

        /// <summary>
        /// Checks if current creature is owning requested items
        /// </summary>
        /// <param name="itemcode">item code </param>
        /// <returns></returns>
        public bool CheckOwnership(string itemcode)
        {
            CreatureData cd = ((CreatureData) Data);
            return cd.L.GetItemByInfoId(itemcode, false) != null;
//            string ownerId = Root.I.GameSystem.FindOwner(itemcode);

  //          return ownerId == Data.Id;
        }

        internal void SetCharToPartyFacing(PartyData attackedParty)
        {
            throw new NotImplementedException();
        }

        public void DecrementChargesRemaining()
        {
			CreatureData cd = ((CreatureData)Data);
			if (cd.CreatureType == CreatureType.Monster) cd.SpellCharges--;
        }

		/// <summary>
		/// Get TileData for room which is in front of creature
		/// 1 - Directly in front of me
		/// 2 - One step away
		/// 3 - ...
		/// </summary>
        public TileData GetRoomInFrontOfMe(int distance)
        {
            //determine where im standing and where im looking
            CreatureData cd = (CreatureData) Data;
            Directions dir = cd.Facing;

            //on what layer am I?
            PartyData pd = cd.L.GetParty();

            if (pd != null)
            {
                FloorData fd = Root.I.FloorSystem.GetData(pd.LayerId);

                if (fd!=null)
                {
                    switch (dir)
                    {
                        case  Directions.North:
                            {
                                // get tile
                                return (TileData) fd.TileLayer[pd.Location.X - distance, pd.Location.Y];
                            }
                        case Directions.West:
                            {
                                return (TileData)fd.TileLayer[pd.Location.X, pd.Location.Y-distance];
                            }
                        case Directions.South:
                            {
                                // get tile
                                return (TileData)fd.TileLayer[pd.Location.X + distance, pd.Location.Y];
                            }
                        case Directions.East:
                            {
                                return (TileData)fd.TileLayer[pd.Location.X, pd.Location.Y + distance];
                            }
                    }
                }
            }
            return null;
        }

		#region Item handling
        public ItemData RemoveObjectFromItemInHand()
        {
            return Root.I.GameSystem.ItemInHand;
        }

        public ItemData CheckItemByInfoId(string infoid)
        {
            return GetItemByInfoId(infoid, false);
        }
		
		/// <summary>
		/// Add this item to inventory
		/// </summary>
		public void AddCharacterPossession(ItemData id)
		{
            Root.I.EffectSystem.Instances.I_PutItemInInventory_Effect.Instant(Data, id);
		}

        /// <summary>
        /// Add this item to inventory
        /// </summary>
        public void AddCharacterPossession(ItemData id, CarryLocations cl)
        {
            CreatureData cd = (CreatureData) Data;
            cd.Inventory.PutItemToPosition(cl, id.Id);
        }
		
		/// <summary>
		/// 
		/// </summary>
		public ItemData Possession(CarryLocations cl)
		{
		    CreatureData cd = (CreatureData) Data;

		    string s = cd.Inventory.GetItemAtPosition(cl);
            if (!string.IsNullOrEmpty(s))
            {
                return Root.I.ItemSystem.GetData(s);
            }

			return null;
		}
		
        public ItemData RemoveCharacterPossession(ItemData itm)
        {
            CreatureData cd = (CreatureData) Data;
            return cd.Inventory.Remove(itm);
        }		

        public ItemData GetItemByInfoId(string infoId, bool remove)
        {
            CreatureData cd = ((CreatureData) Data);
            ItemData id = cd.Inventory.GetItemByInfoId(infoId, remove);
            if (id != null) return id;
            return null;
        }
		
		#endregion
		
		public void TenSecondUpdate()
		{
			  int D0L, D0W, D1L, D1W, D3L, D3W, D4L, D4W, D5L, D5W, D6L, D6W;
			  ItemData objNID0;
			  int pwA0, pwA1;               // toto je pointer kamsi
			  CurrentNormal ptA2;
			    CreatureData pcA3 = (CreatureData) Data;
                PartyData creaturesParty = pcA3.L.GetParty();
			  int LOCAL_26;
			  //i16 LOCAL_16;
			  int LOCAL_6 = 0x2bad; // Party location.
			  int LOCAL_4;
			  int random112; //sort of random number 0 to 112; i16 LOCAL_2;
			  int chIdx;


            //tomuhle bloku vubec nerozumim co dela
			//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
              ////if (d.NumCharacter == 0) return;
              //D3W = (int)(d.partyX & 31);
              //LOCAL_6 = sw((LOCAL_6 & 0xffe0) | D3W);
              //D3W = (int)(d.partyY & 31);
              //LOCAL_6 = (LOCAL_6 & 0xfc1f) | (D3W << 5);
			  
            // co dela nasledujici radek?
            //LOCAL_6 = (LOCAL_6 & 0x03ff) | ((d.partyLevel&63)<<10);

            //ddta

              //for (D6W=0; D6W<d.NumFootprintEnt-1; D6W++)
              //{
              //  pwA1 = &d.PartyFootprints[D6W];
              //  pwA0 = &LOCAL_6;
              //  D0L = 0;
              //  //This does nothing that I can see.
              //  //SearchWordNE(&D0, &pwA0, pwA1);
              //  D0W = sw((UI8)(d.Byte13220[D6W]) - 1);
              //  D0W = sw(Larger(0, D0W));
              //  d.Byte13220[D6W] = D0B;
              //  if (D0B != 0) continue;
              //  if (D6W != 0) continue;
              //  DeleteFootprintEntry(0);
              //  break;

              //};

              random112 = (creaturesParty.Time & 0x40) | ((creaturesParty.Time & 0x80) >> 2) | ((creaturesParty.Time & 0x100) >> 4);
			  //D7W = 0;
			  //pcA3 = &d.CH16482;

			  // zde se logika aplikuje pro kazdeho hrace v parte. Ja mam hru nastavenou tak,
			  // ze i monstrum muze byt hrac a tedy je toto pro me cesta nepruchozi
			  //for (chIdx=0; chIdx < d.NumCharacter; chIdx++)
		    chIdx = 0;
			  {
				
				// jestli ma dany hrac 0 zivotu tak nedelej nic. Je mrtvej
				if (pcA3.Attributes.Health.Current == 0) return;
				//if (chIdx + 1 == d.PotentialCharacterOrdinal) continue;
				
				// nemam plnou manu
				if (pcA3.Attributes.Mana.Current < pcA3.Attributes.Mana.Max)
				{

				  //zjisti kolik mam doiplnit many na zaklade WIZARD a PRIEST levelu + Wisdom current
				  int magicMastery, magicMasteryPlusWisdom;
				  magicMastery = 
                      Calculations.DetermineMastery(pcA3, chIdx, (int)SkillNames.SKILL_Wizard) + 
                      Calculations.DetermineMastery(pcA3, chIdx, (int)SkillNames.SKILL_Priest);
				  
                  magicMasteryPlusWisdom = magicMastery + pcA3.Attributes.Wisdom.Current;
				  //D0L = random112; // Reversed 3 bits of time
				  
				  //?
				  if (random112 >= magicMasteryPlusWisdom)// goto tag0179b0;
				  {
				    // zde jsem ve stavu kdy mam many moc a tedy postupne manu snizuji.
					if (pcA3.Attributes.Mana.Current > pcA3.Attributes.Mana.Max)
					{
					  pcA3.Attributes.Mana.Incr(-1);
					}
				  }
				  else
				  {
					//////////////////////////////////// MANA++
				    // manaAdjust = MaxMana / 40;
					int manaAdjust, staminaAdjust;
					manaAdjust = (pcA3.Attributes.Mana.Max) / 40;
		            if (creaturesParty.IsPartySleeping)
					{
					  manaAdjust *= 2;
					};
					
					manaAdjust++;

					//////////////////////////////////// STAMINA++
					staminaAdjust = manaAdjust * Calculations.Larger(7, 16-magicMastery);

					// Tahle metoda dela to ze kdyz je stamina mensi nez 0, zacne se hraci ublizovat.
					Calculations.AdjustStamina(pcA3, staminaAdjust);
					pcA3.Attributes.Mana.Current = 
                        pcA3.Attributes.Mana.Current + 
                        Calculations.Smaller(manaAdjust, pcA3.Attributes.Mana.Max-pcA3.Attributes.Mana.Current);
				  }
				}
				else
				{
				  //Mam vic jak plno ... ubiram o jedna
				  if (pcA3.Attributes.Mana.Current > pcA3.Attributes.Mana.Max)
				  {
					pcA3.Attributes.Mana.Incr(-1);
				  }
				}

				//////////////////////////////////// UPRAVA SKILLU
				for (D4W=19; D4W>=0; D4W--)
				{
				  //docasny boost jde o jedna dolu
				  if (pcA3.Skills[D4W].tempAdjust > 0)
				  {
					pcA3.Skills[D4W].tempAdjust--;
				  };
				};
				
				//zajimava konstrukce....uvidime jestli ji sezere C#
                //for (
                //    D6W=4, 
                //    D4W=pcA3.Attributes.Strength.Max;
                //        D4W>>=1, LOCAL_26=D4W, pcA3.Attributes.Stamina.Current<LOCAL_26;
                //     D6W+=2)
                //{

                // az dam dokupy ten blok vyse zakomentovat nasledujici radek
			      D6W = 4;

                //}
				D4W = 0;
				
				// Limit to between 1 and 6
				LOCAL_4 = Calculations.ApplyLimits(1, (pcA3.Attributes.Stamina.Max>>8)-1, 6);
				if (creaturesParty.IsPartySleeping)
				{
				  LOCAL_4 <<= 1;
				}
				
				//pocet jednotek od posledniho pohybu party
				D5W = (creaturesParty.Time-creaturesParty.LastPartyMoveTime);
				if (D5W > 80)
				{
				  LOCAL_4++;
				  if (D5W > 250)
				  {
					LOCAL_4++;
				  }
				}
				
				//////////////////////////////////// JIDLO (max mam nastaveno v Attrbutes na 1500)
				do
				{
				  D5W = (D6W <= 4) ? 1 : 0;
				  
				  //starwing?
				  if (pcA3.Attributes.Food.Current < -512)
				  {
					if (D5W==1)
					{
					  D4W = D4W + LOCAL_4;
					  pcA3.Attributes.Food.Current -= 2;
					};
				  }
				  else
				  {
					if (pcA3.Attributes.Food.Current >= 0)
					{
					  D4W = D4W - LOCAL_4;
					}
					if (D5W==1)
					{
					  D0W = 2;
					}
					else
					{
					  D0W = D6W / 2;
					}
					pcA3.Attributes.Food.Current = pcA3.Attributes.Food.Current - D0W;
				  }
				  
				  //////////////////////////////////// WATER (max mam nastaveno v Attrbutes na 1500)
				  if (pcA3.Attributes.Water.Current < -512)
				  {
					if (D5W==1)
					{
					  D4W = D4W + LOCAL_4;
					  pcA3.Attributes.Water.Current--;
					}
				  }
				  else
				  {
					if (pcA3.Attributes.Water.Current >= 0)
					{
					  D4W = D4W - LOCAL_4;
					}
					if (D5W==1)
					{
					  D0W = 1;
					}
					else
					{
					  D0W = D6W / 2;
					}
					pcA3.Attributes.Water.Current = pcA3.Attributes.Water.Current - D0W;
				  }
				  D6W--;
				  if (D6W == 0) break;
				} while (pcA3.Attributes.Stamina.Current < pcA3.Attributes.Stamina.Max + D4W);

				Calculations.AdjustStamina(pcA3, D4W);

				// nastaveni minima jidla tak, aby bylo schopne jedenim se vratit na realne hodnoty
		        if (pcA3.Attributes.Food.Current < -1024)   pcA3.Attributes.Food.Current = -1024;
                if (pcA3.Attributes.Water.Current < -1024)  pcA3.Attributes.Water.Current = -1024;
				
				// nemam plny zivot
				if (pcA3.Attributes.Health.Current < pcA3.Attributes.Health.Max)
				{
				  D0W = pcA3.Attributes.Stamina.Current;
				  D1W = pcA3.Attributes.Stamina.Max / 4;
				  if (D0W >= D1W)
				  {
					D0L = random112;
					D1W = pcA3.Attributes.Vitality.Current + 12;

					if (D0L < D1W)
					{
					  LOCAL_4 = (pcA3.Attributes.Stamina.Max >> 7) + 1;
					  if (creaturesParty.IsPartySleeping)
					  {
						LOCAL_4 = 2 * LOCAL_4;
					  }
					  
                        objNID0 = pcA3.L.Possession(CarryLocations.Necklace);//.NameIndex()); //necklace
                      //if (objNID0 == objNI_EkkhardCross)
                      //{
                      //  LOCAL_4 = sw(LOCAL_4 + (LOCAL_4 / 2) + 1);
                      //};
					  D0W = Calculations.Smaller(
                          LOCAL_4, 
                          pcA3.Attributes.Health.Max-pcA3.Attributes.Health.Current);

					  pcA3.Attributes.Health.Current = pcA3.Attributes.Health.Current + D0W;
					}
				  }
				}
				
                //// ?
                //if (d.Word11750)
                //{
                //  d.Word11750--;
                //  if (d.Word11750 == 0)
                //  {
                //    d.CanRestartFromSavegame = 0;
                //    DamageAllCharacters(0x1000, 0, 0);
                //  };
                //};
				
				
				D0L = creaturesParty.Time;
				if (creaturesParty.IsPartySleeping)
				{
				  D1W = 63;
				}
				else
				{
				  D1W = 255;
				};
				
				/////////////////////////////// UPRAVA ATRIBUTU
				if ((D0L & D1W) == 0)
				{

				  for (D6W=0; D6W<=6; D6W++)
				  {
					ptA2 = pcA3.Attributes[D6W];
					D5W = ptA2.Max;
					D0W = ptA2.Current;

					if (D0W < D5W)
					{
					  ptA2.Current++;
					}
					else
					{
					    D0W = ptA2.Current;
					  if (D0W > D5W)
					  {
						D0L = ptA2.Current / ((D5W==0)?1:D5W);
						ptA2.Incr(-D0W);
					  }
					}
				  }
				}
				
				
				if (!creaturesParty.IsPartySleeping)
				{
					// paklize se spac kouka jinam nez parta
				  D0W = (int)pcA3.Facing;
				  if (D0W != (int)creaturesParty.Facing)
				  {
					D0L = creaturesParty.LastMonsterAttackTime;
					D1L = creaturesParty.Time - 60; // About 10 seconds
					if (D0L < D1L)
					{
						// a delsi dobu na nas uz nikdo neutoci...
						// tak nas zarovej stejne jako zbytek party.
					  pcA3.Facing = creaturesParty.Facing;
					  pcA3.MaxRecentDamage = 0;
					  pcA3.CharFlags |= CHARFLAGS.CHARFLAG_positionChanged;
					};
				  };
				};
				
				
				pcA3.CharFlags |= CHARFLAGS.CHARFLAG_statsChanged;
				D0W = chIdx + 1;
                //D1W = d.SelectedCharacterOrdinal;
                //if (D0W == D1W)
                //{
                //  if (   (d.PressingMouth)
                //      || (d.PressingEye)
                //      || (d.DisplayResurrectChestOrScroll==0) )
                //  {
                //    pcA3->charFlags |= CHARFLAG_chestOrScroll;
                //  }
                //}
			  }
		}

		/// <summary>
		/// Every creature have to be in party. This method will tell us which party creature is in
		/// </summary>
        public PartyData GetParty()
        {
            foreach(PartyData pd in Root.I.PartySystem.DictData.Values)
            {
                foreach(CreatureData cd in pd.MembersData)
                {
                    if (cd.Id == Data.Id) return pd;
                }
            }

            return null;
        }
		
		public void SleepStart()
		{
			tTenSecondUpdateTimer.Interval = 2500;
			
	        //if (OnSleepStarted != null)
            //{
            //    OnSleepStarted(null, null);
            //}			
		}
		
		public void SleepEnd()
		{
            tTenSecondUpdateTimer.Interval = 10000;
			
            //if (OnSleepEnded != null)
            //{
            //    OnSleepEnded(null, null);
            //}			
		}
		
		/// <summary>
		/// This method will disable action panel for specied amount of time and
		/// handles action panel enable after desired interval. Other actions can
		/// increase duration and this method will respect it.
		/// </summary>
		public void DisableActionPanels(int intervalInSeconds)
		{
			// add new duration
			((CreatureData)Data).DisabledDuration +=intervalInSeconds;
			
            Timer disableTimer = new Timer(1000);
            disableTimer.Elapsed += delegate
            {
				((CreatureData)Data).DisabledDuration--;
				
				if (((CreatureData)Data).DisabledDuration==-1) Stop();
            };
            disableTimer.Start();				
		}
		

    }
}
